/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2005-2007 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AAC_INT_AUDIO_DECODER)

#include <ipps.h>
#include <ippac.h>
#include <math.h>
#include "sbr_settings.h"
#include "sbr_dec_struct.h"
#include "sbr_dec_api_int.h"
#include "sbr_dec_tabs_int.h"
#include "sbr_dec_own_int.h"

/********************************************************************/

#ifndef ID_SCE
#define ID_SCE    0x0
#endif

#ifndef ID_CPE
#define ID_CPE    0x1
#endif

/********************************************************************/

static Ipp32s sbrPassiveUpsampling(Ipp32s* XBuf,
                                   Ipp32s* iZBuf,
                                   Ipp32s mode);

/********************************************************************/

static Ipp32s sbrActiveUpsampling(Ipp32s* XBuf,
                                  Ipp32s* iYBuf,
                                  Ipp32s xoverBand,
                                  Ipp32s* iZBuf,
                                  Ipp32s mode);

/********************************************************************/

/* Set HF subbands to zero */
Ipp32s sbriCleanHFBand(Ipp32s** pYBuf, Ipp32s startBand, Ipp32s stopBand, Ipp32s mode);

/********************************************************************/

static Ipp32s sbrUpDateBands(Ipp32s** ppSrc );

/********************************************************************/

static Ipp32s sbrUpdateAmpRes(Ipp32s bs_frame_class, Ipp32s L_E, Ipp32s bs_amp_res)
{
  if ((bs_frame_class == FIXFIX) && (L_E == 1))
    bs_amp_res = 0;

  return bs_amp_res;
}

/********************************************************************/

Ipp32s sbridecReset(sSBRBlock* pSbr)
{
  IppStatus status;

  sbrdecResetCommon( &(pSbr->comState) );

  // FIXED-POINT
  status = ippsZero_32s(pSbr->wsState.iBufGain[0][0],  2*MAX_NUM_ENV*MAX_NUM_ENV_VAL);
  status = ippsZero_32s(pSbr->wsState.iBufNoise[0][0], 2*MAX_NUM_ENV*MAX_NUM_ENV_VAL);
  status = ippsZero_32s(pSbr->wsState.bwArray[0],      2*MAX_NUM_NOISE_VAL);

  return 0;//OK
}

/********************************************************************/

void sbriDecoderGetSize(Ipp32s *pSize)
{

  Ipp32s Size32 = (32) * (NUM_TIME_SLOTS * RATE + SBR_TIME_HFGEN);
  Ipp32s Size64 = (64) * (NUM_TIME_SLOTS * RATE + SBR_TIME_HFGEN);

  pSize[0] = (((Size32 + Size64) * 2) * sizeof(Ipp32s) * 2) * CH_MAX;

}

/********************************************************************/

void sbriInitDecoder(sSBRBlock* pState[CH_MAX], void* pMem)
{
  Ipp32s  ch, i, j;
  sSBRBlock *pBlock;
  Ipp32s *pData = (Ipp32s *)pMem;
  Ipp32s bufLen;

  Ipp32s Size32 = (32) * (NUM_TIME_SLOTS * RATE + SBR_TIME_HFGEN);
  Ipp32s Size64 = (64) * (NUM_TIME_SLOTS * RATE + SBR_TIME_HFGEN);

  bufLen = (Size32 + Size64) * 2; /* in floats */

  /* --------------------- set memory for matrix --------------------- */
  for (i = 0; i < CH_MAX; i++) {
    pBlock = pState[i];
    ippsZero_8u((Ipp8u*)pBlock, sizeof(sSBRBlock));

    for (ch = 0; ch < 2; ch++) {
      pBlock->wsState.iXBuf[ch][0] = pData;
      ippsZero_32s(pBlock->wsState.iXBuf[ch][0], bufLen);

      /* process need because mixing memory will be done */
      pBlock->wsState._dcMemoryMatrix[ch] = 0;
      pBlock->wsState._dcMemoryMatrix[ch] = pBlock->wsState.iXBuf[ch][0];

      for (j = 0; j < (NUM_TIME_SLOTS * RATE + SBR_TIME_HFGEN); j++) {
        pBlock->wsState.iXBuf[ch][j] = pBlock->wsState.iXBuf[ch][0] + j * (2*32);
        pBlock->wsState.iYBuf[ch][j] = pBlock->wsState.iXBuf[ch][0] + (2*Size32 + 0 * Size64)+ j * (2*64);
      }
      pData += bufLen;
    }
    /* --  set default values -- */
    sbridecReset(pBlock);
    pBlock->comState.sbrHeaderFlagPresent = 0;
  }
}

/********************************************************************/

Ipp32s sbriFreeDecoder(sSBRBlock * pSbr)
{
  Ipp32s  ch;

  if( pSbr == 0 )
    return 0;

  for (ch = 0; ch < 2; ch++) {
    if( pSbr->wsState._dcMemoryMatrix[ch] )
      ippsFree( (Ipp32s*)(pSbr->wsState._dcMemoryMatrix[ch]) );
  }

  ippsFree(pSbr);

  return 0;     // OK
}

/********************************************************************/

Ipp32s sbriGetFrame(Ipp32s *pSrc, Ipp32s *pDst,
                    sSBRBlock * pSbr, sSbrDecFilter* sbr_filter,
                    Ipp32s ch, Ipp32s decode_mode, Ipp32s dwnsmpl_mode,
                    Ipp8u* pWorkBuffer, Ipp32s* scaleFactor )
{
  sSBRDecComState* com   = &(pSbr->comState);
  sSBRDecWorkState* ws = &(pSbr->wsState);

  Ipp32s startBand = RATE * com->sbrFIState[ch].bordersEnv[0];
  Ipp32s stopBand = 0;
  Ipp32s transitionBand = pSbr->comState.transitionBand[ch];
  Ipp32s l;
  Ipp32s bs_amp_res;
  Ipp32s my_ch;
  Ipp32s degPatchedCoefs[64];

  Ipp32s bufZ[2*64];

  Ipp32s xScale = (dwnsmpl_mode == HEAAC_DWNSMPL_OFF) ? 64 : 32;

  /* -------------------------------- SCALE DATA ------------------------ */

  {
    Ipp32s shift = (SCALE_FACTOR_QMFA_IN - *scaleFactor - 2);
    ippsLShiftC_32s_I(shift, pSrc, 1024);
  }

/* -------------------------------- Analysis ---------------------------- */
  if (HEAAC_HQ_MODE == decode_mode) {

    sbri_ownAnalysisFilter_SBRHQ_32s_D2L(pSrc,
                                    (Ipp32sc**)(ws->iXBuf[ch]) + SBR_TIME_HFGEN,
                                    32,
                                    com->kx,
                                    (IppsFilterSpec_SBR_C_32sc*)(sbr_filter->pMemSpecQMFA[ch]));

  }else{ //if (HEAAC_LP_MODE == decode_mode)
    for(l=0; l<NUM_TIME_SLOTS * RATE; l++) {

      /*********************************************************************
       * NOTE:
       * (1) matrix idea is better than 1D
       *     but if you can use this function for non-matrix,
       *     you must use such consrtuctions
       * (2) you can use non-standard function of window and get better
       *     result (speech codec or other area)
       *********************************************************************/
       sbri_ownAnalysisFilter_SBRLP_32s_D2L(pSrc + l*32,
                                              ws->iXBuf[ch] + SBR_TIME_HFGEN + l,
                                              1, com->kx,
                                              (IppsFilterSpec_SBR_R_32s*)(sbr_filter->pMemSpecQMFA[ch]));

    }
  }

/* -------------------------------- <SBR process> ---------------------------- */
  if ((com->sbrHeaderFlagPresent != 0) && (com->sbrFlagError == 0) &&
      ((com->id_aac == ID_SCE) || (com->id_aac == ID_CPE))) {

        stopBand = RATE * com->sbrFIState[ch].bordersEnv[ com->sbrFIState[ch].nEnv ];

        /* skip couple channel for ch == R == 1 */
        if ( !ch || !com->bs_coupling )
        {
          bs_amp_res = sbrUpdateAmpRes( com->bs_frame_class[ch], com->sbrFIState[ch].nEnv, com->sbrHeader.bs_amp_res);

          sbriDequantization( com, ws, ws->sfsEOrig[ch], ch, bs_amp_res );
        }

        /* Set HF subbands to zero */
        sbriCleanHFBand(ws->iYBuf[ch], startBand, stopBand, decode_mode);


        sbriGenerationHF(ws->iXBuf[ch],
                         ws->iYBuf[ch],
                         &(pSbr->comState), ws->bwArray[ch],
                         degPatchedCoefs, ch, decode_mode);


        if (HEAAC_LP_MODE == decode_mode) {
          for (l = startBand; l < stopBand; l++) {
            ippsZero_32s(ws->iYBuf[ch][SBR_TIME_HFADJ + l], com->kx);
          }
        }

    my_ch = (com->bs_coupling == 1) ? 0 : ch;

    sbriAdjustmentHF(ws->iYBuf[ch],
                     ws->bufEnvOrig[ch], ws->bufNoiseOrig[ch],
                     ws->sfsEOrig[my_ch],
                     ws->iBufGain[ch], ws->iBufNoise[ch],
                     &(pSbr->comState), degPatchedCoefs, pWorkBuffer,
                     com->sbrHeader.Reset, ch, decode_mode );

    for (l = 0; l < NUM_TIME_SLOTS * RATE; l++) {

      Ipp32s xoverBand = ( l < transitionBand ) ? com->kx_prev : com->kx;

      sbrActiveUpsampling(ws->iXBuf[ch][l + SBR_TIME_HFADJ],
                          ws->iYBuf[ch][l + SBR_TIME_HFADJ],
                          xoverBand,
                          bufZ, decode_mode);

      /* may be is used pointer to fuction instead CASE */
      sbriSynthesisFilter_32s(bufZ, pDst + xScale*l,
                             sbr_filter->pMemSpecQMFS[ch],
                             scaleFactor,
                             decode_mode | dwnsmpl_mode );


    }

    /* UpDate High Band */
    sbrUpDateBands(ws->iYBuf[ch]);

// store ch independ
    com->transitionBand[ch] = stopBand - NUM_TIME_SLOTS * RATE;

  } else {

    //--------------
    for (l = 0; l < NUM_TIME_SLOTS * RATE; l++) {

      sbrPassiveUpsampling(ws->iXBuf[ch][l + SBR_TIME_HFADJ],
                           bufZ, decode_mode);

      /* may be is use pointer to fuction instead CASE */
      sbriSynthesisFilter_32s(bufZ, pDst + xScale*l,
                              sbr_filter->pMemSpecQMFS[ch],
                              scaleFactor,
                              decode_mode | dwnsmpl_mode );
    }
    //--------------
  }

   /* UpDate Low Band */
   sbrUpDateBands( ws->iXBuf[ch] );

/* ---------------------------- <store ch depend> ---------------------------- */

  if ((com->id_aac == ID_SCE) || ((com->id_aac == ID_CPE) && (ch == 1))) {
    com->kx_prev = com->kx;
    com->M_prev = com->M;
    com->sbrHeader.Reset = 0;
  }

  return 0;     // OK
}

/********************************************************************/

static Ipp32s sbrPassiveUpsampling(Ipp32s* iXBuf,
                                   Ipp32s* iZBuf,
                                   Ipp32s mode)
{
  Ipp32s xScale = (HEAAC_LP_MODE == mode) ? 1 : 2;

  ippsCopy_32s(iXBuf, iZBuf, NUM_TIME_SLOTS * RATE * xScale);
  ippsZero_32s( iZBuf + 32 * xScale, NUM_TIME_SLOTS * RATE * xScale);

  return 0;     // OK
}

/********************************************************************/

static Ipp32s sbrActiveUpsampling(Ipp32s* iXBuf,
                                  Ipp32s* iYBuf,
                                  Ipp32s xoverBand,
                                  Ipp32s* iZBuf,
                                  Ipp32s mode)
{
  Ipp32s k;
  Ipp32s xScale = (HEAAC_LP_MODE == mode) ? 1 : 2;

  for (k = 0; k < xoverBand * xScale; k++) {

    iZBuf[k] = iXBuf[k];
  }

  if (mode == HEAAC_LP_MODE) {
    iZBuf[xoverBand - 1]   += iYBuf[xoverBand - 1];
  }

  for (k = xoverBand * xScale; k < 64 * xScale; k++) {
    iZBuf[k] = iYBuf[k];
  }

  return 0;     // OK
}

/********************************************************************/

/* Set HF subbands to zero */
Ipp32s sbriCleanHFBand(Ipp32s** pYBuf, Ipp32s startBand, Ipp32s stopBand, Ipp32s mode)
{
  Ipp32s i;
  Ipp32s xScale = (HEAAC_LP_MODE == mode) ? 1 : 2;

  for (i = startBand; i < stopBand; i++) {
    ippsZero_32s(pYBuf[i + SBR_TIME_HFADJ], 64 * xScale);
  }

  return 0; //OK
}

/********************************************************************/

static Ipp32s sbrUpDateBands(Ipp32s** ppSrc )
{
  Ipp32s l;
  Ipp32s* pBufTmp;

  for (l = 0; l < SBR_TIME_HFGEN; l++) {
    // Re part
    pBufTmp    = ppSrc[l];
    ppSrc[l] = ppSrc[NUM_TIME_SLOTS * RATE + l];
    ppSrc[NUM_TIME_SLOTS * RATE + l] = pBufTmp;
  }

  return 0;
}

/********************************************************************/

#endif //UMC_ENABLE_AAC_INT_AUDIO_DECODER

